package it.conv;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Locale;

public class ConvApp {
	
	//Valore massimo di Es/N0
	static final double MAXESN0 = 3.5;
	//Valore minimo di Es/N0
	static final double MINESN0 = 0.5;
	//Incremento minimo dei valori di Es/N0
	static final double STEP = 0.5;
	static final int BERiter = (int)((MAXESN0 - MINESN0) / STEP) + 1;
	
	/*
	 * Rate di bit in/out per ogni in bit, ho out bit in uscita
	 * con out > in (nel nostro caso in sempre = 1) e out può
	 * essere solo 2, 3 o 4
	 */
	static final int IN_BIT = 1;
	static int OUT_BIT;
	
	//Rapporto potenza per ogni bit / rumore
	static double EsSuN0;
	//Lunghezza del messaggio trasmesso
	static int LUN_CANALE;
	//Lunghezza di ogni messaggio
	static int LUN_MESSAGGIO;
	/*
	 * Parametro m del codificatore convoluzionale compreso tra 3 e 10
	 */
	static int M;
	//Polinomio generatore
	static int[][] g;
	
	private static int[] pacchetto;
	private static int[] pakCodificato;
	private static double[] pakRumore;
	private static int[] pakDecodificato;
	
	static DecimalFormat df2s;
	static DecimalFormat df2;
	
	static {
		
		df2s = (DecimalFormat)DecimalFormat.getInstance(Locale.US);
		df2s.applyPattern("'+'##.##; '-'##.##");
		
		
	}
	
	static {
		
		df2 = (DecimalFormat)DecimalFormat.getInstance(Locale.US);
		df2.applyPattern("##.##");
		
		
	}
	
	//Inizializzazione del polinomio generatore dato m
	private static void inizializza(int m, int output, int lun) {
		
		M = m;
		OUT_BIT = output;
		LUN_MESSAGGIO = lun;
		LUN_CANALE = (LUN_MESSAGGIO + M - 1) * OUT_BIT;
		
		try {
			
			g = PolyGenerator.genera(M,OUT_BIT);
			
		} catch (Exception e) {
			
			System.out.println(e.getMessage());
			System.exit(-1);
			
		}
		
	}
	
	public static void stampaDati(int[] a) {
		
		for (int i = 0; i < a.length; i++) {
			System.out.print(a[i]);
			if ((i + 1) % 60 == 0)
				System.out.println();
		}
		
	}
	
	public static void stampaDati(double[] a) {
		
		for (int i = 0; i < a.length; i++) {
			System.out.print(df2s.format(a[i]) + " ");
			if ((i + 1) % 20 == 0)
				System.out.println();
		}
		
	}

	public static void main(String[] args) {
		
		if (args.length < 3) {
			
			System.out.println("PARAMETRI");
			System.out.println("1) m");
			System.out.println("2) bit in output");
			System.out.println("3) Lunghezza del messaggio!");
			System.exit(-1);
			
		} else {
			
			inizializza(Integer.parseInt(args[0]),Integer.parseInt(args[1]),
					Integer.parseInt(args[2]));
			
			System.out.println("\t\tINIZIO\n\n");
			
			System.out.println("Rate = " + IN_BIT + "/" + OUT_BIT);
			System.out.println("m = " + M);
			System.out.println("Lunghezza messaggio = " + LUN_MESSAGGIO);
			for (int i = 0; i < g.length; i++) {
				System.out.print("Polinomio g" + i + " = [ ");
				for (int j = 0; j < g[i].length; j++)
					System.out.print( g[i][j] + " ");
				System.out.println("]");
			}
			
			int k = 0;
			double[] BER = new double[BERiter];
			long[] T = new long[BERiter];
			int[] I = new int[BERiter];
			
			//Si testano i differenti valori di Es / N0
			for (EsSuN0 = MINESN0; EsSuN0 <= MAXESN0; EsSuN0 += STEP, k++) {
				
				/*
				 * eBER = Bit Error Ratio, del segnale codificato
				 * soglia = soglia di errori che varia dal valore di Es / N0
				 */
				double soglia = 40 / (EsSuN0 + 0.5);
				int  erroriTotali = 0;
				I[k] = 0;
				T[k] = 0;
				
				long tini = System.currentTimeMillis();
				DataGenerator.inizializza(EsSuN0, M, OUT_BIT,System.out);
								
//				if (EsSuN0 <= 9)
//					soglia = 100;
//				else
//					soglia = 20;
				
				while (erroriTotali < soglia) {
					
					I[k]++;
					
					System.out.println("\nGenerazione dati...");
					pacchetto = DataGenerator.generaDati(LUN_MESSAGGIO);
					
//					System.out.println("Dati generati:");
//					stampaDati(pacchetto);
					
					System.out.println("\nCodifica convoluzionale...");
					Encoder enc = new Encoder(pacchetto, M, g, OUT_BIT, LUN_MESSAGGIO);
					pakCodificato = enc.encode();
					
//					System.out.println("Dati codificati:");
//					stampaDati(pakCodificato);
					
					System.out.println("\nAggiunta rumore...");
					pakRumore = DataGenerator.aggiungiRumore(pakCodificato, LUN_CANALE, true);
					
//					System.out.println("Dati con rumore:");
//					stampaDati(pakRumore);
					
					System.out.println("\nDecodifica con algoritmo Viterbi...");
					Decoder dec = new Decoder(pakRumore, M, g, OUT_BIT, LUN_MESSAGGIO);
					pakDecodificato = dec.decode();
					
					T[k] += System.currentTimeMillis() - tini;
					
//					System.out.println("\nDati decodificati con errori:");
					
					int err = 0;
					for (int i = 0; i < LUN_MESSAGGIO; i++) {
						
						if (pacchetto[i] != pakDecodificato[i]) {
							
//							System.out.print("_");
							++err;
							
						}
//						else
//							System.out.print(pakDecodificato[i]);
//						
//						if ((i + 1) % 60 == 0)
//							System.out.println();
						
					}
					
					erroriTotali += err;
					System.out.println("\nErrori nell'iterazione " + I[k] + ": " + err);
					System.out.println("Errori totali: " + erroriTotali);
					
					try {

						System.out.println("\nPremere ENTER per continuare...");
						System.in.read();
						
						if (System.in.available() > 0)
							System.in.read();
						
					} catch (IOException io) {
						
						io.printStackTrace();
						
					}
					
				}
				
				BER[k] = ((double) erroriTotali) /
						((double) (LUN_MESSAGGIO * I[k]));
				
			}
			
			k = 0;
			for (double i = MINESN0; i < MAXESN0; i += STEP, k++) {
				
				System.out.println("\nCon Es/N0 = " + i + " il BER è stato " + BER[k]);
				
				System.out.println("Il tempo trascorso è stato di " + 
					T[k] + " secondi per " + I[k] + " iterazioni\n");
				
			}
			
			System.out.println("\n\t\tFINE");
			System.exit(0);
			
		}
		
	}
	
}
